{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 前馈神经网络编程练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch \n",
    "import numpy as np\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision.datasets import mnist\n",
    "from torch import nn\n",
    "from torch import optim\n",
    "from torchvision import transforms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "设计一个模型名叫net，该模型由三层组成，每层都是全连接层组成，在__init__()中实现；\n",
    "定义完模型结构，还要去实现该模型前向传播的过程：forward（）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "class net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(net,self).__init__()\n",
    "        \n",
    "        self.layer1 = nn.Sequential(\n",
    "                nn.Linear(784, 200), # 28*28=784 因为mnist一张图片是1*28*28的图片\n",
    "                nn.BatchNorm1d(200),\n",
    "                nn.ReLU(inplace=True))\n",
    "        \n",
    "        self.layer2 = nn.Sequential(\n",
    "                nn.Linear(200, 100),\n",
    "                nn.BatchNorm1d(100),\n",
    "                nn.ReLU(inplace=True))\n",
    "        \n",
    "        self.layer3 = nn.Linear(100, 10)\n",
    "        \n",
    "    def forward(self,x):\n",
    "        x = x.view(x.size(0),-1)\n",
    "        x = self.layer1(x)\n",
    "        x = self.layer2(x)\n",
    "        x = self.layer3(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据增强，将读取进来的mnist数据进行数据增强，归一化等操作。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_tf = transforms.Compose(\n",
    "                [transforms.ToTensor(),\n",
    "                 transforms.Normalize([0.5],[0.5])])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "读取数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_set = mnist.MNIST('./data',train=True,transform=data_tf,download=True)\n",
    "test_set = mnist.MNIST('./data',train=False,transform=data_tf,download=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "用刚刚得到的数据集生成dataloader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data = DataLoader(train_set,batch_size=64,shuffle=True)\n",
    "test_data = DataLoader(test_set,batch_size=64,shuffle=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "指定损失函数和优化器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "net = net()\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "optimizer = optim.SGD(net.parameters(),1e-1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1: \n",
      "Train Loss: 0.027089773018078418 \tTrain Accuracy: 0.9915378464818764 \n",
      "Test Loss: 0.080875120454334 \tTest Accuracy: 0.9747213375796179\n",
      "\n",
      "Epoch 2: \n",
      "Train Loss: 0.025195651932302783 \tTrain Accuracy: 0.9919542910447762 \n",
      "Test Loss: 0.0761797726351288 \tTest Accuracy: 0.977906050955414\n",
      "\n",
      "Epoch 3: \n",
      "Train Loss: 0.019156165279771712 \tTrain Accuracy: 0.994186433901919 \n",
      "Test Loss: 0.0805488198305162 \tTest Accuracy: 0.9767117834394905\n",
      "\n",
      "Epoch 4: \n",
      "Train Loss: 0.017917822837009965 \tTrain Accuracy: 0.9945362473347548 \n",
      "Test Loss: 0.08854370586059478 \tTest Accuracy: 0.9746218152866242\n",
      "\n",
      "Epoch 5: \n",
      "Train Loss: 0.013830813569022216 \tTrain Accuracy: 0.9958522121535182 \n",
      "Test Loss: 0.07961334371290954 \tTest Accuracy: 0.9784036624203821\n",
      "\n",
      "Epoch 6: \n",
      "Train Loss: 0.011360268998199325 \tTrain Accuracy: 0.9967184168443497 \n",
      "Test Loss: 0.0861000592299119 \tTest Accuracy: 0.9762141719745223\n",
      "\n",
      "Epoch 7: \n",
      "Train Loss: 0.010785888464474029 \tTrain Accuracy: 0.9969849413646056 \n",
      "Test Loss: 0.07543072853173251 \tTest Accuracy: 0.9781050955414012\n",
      "\n",
      "Epoch 8: \n",
      "Train Loss: 0.009618179720441954 \tTrain Accuracy: 0.9971681769722814 \n",
      "Test Loss: 0.0822591031853341 \tTest Accuracy: 0.9772093949044586\n",
      "\n",
      "Epoch 9: \n",
      "Train Loss: 0.00905529564381319 \tTrain Accuracy: 0.9971681769722814 \n",
      "Test Loss: 0.10199536646876507 \tTest Accuracy: 0.9730294585987261\n",
      "\n",
      "Epoch 10: \n",
      "Train Loss: 0.009609652607136894 \tTrain Accuracy: 0.9973180970149254 \n",
      "Test Loss: 0.08372252877921177 \tTest Accuracy: 0.9776074840764332\n",
      "\n"
     ]
    }
   ],
   "source": [
    "nums_epoch = 10\n",
    "losses =[]\n",
    "acces = []\n",
    "eval_losses = []\n",
    "eval_acces = []\n",
    "\n",
    "for epoch in range(nums_epoch):\n",
    "    train_loss = 0\n",
    "    train_acc = 0\n",
    "    net = net.train()\n",
    "    for img, label in train_data:\n",
    "        # 前向传播\n",
    "        out = net(img)\n",
    "        loss = criterion(out,label)\n",
    "        \n",
    "        # 反向传播\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        # 累计损失\n",
    "        train_loss += loss.item()\n",
    "        \n",
    "        # 计算准确率\n",
    "        _,pred = out.max(1)\n",
    "        num_correct = (pred == label).sum().item()\n",
    "        acc = num_correct / img.shape[0]\n",
    "       \n",
    "        train_acc += acc\n",
    "        \n",
    "    losses.append(train_loss / len(train_data))\n",
    "    acces.append(train_acc / len(train_data))\n",
    "    \n",
    "    eval_loss = 0\n",
    "    eval_acc = 0\n",
    "    \n",
    "    # 在测试集上评估\n",
    "    for img, label in test_data:\n",
    "        \n",
    "        out = net(img)\n",
    "        \n",
    "        loss = criterion(out,label)\n",
    "        \n",
    "        # 累计损失\n",
    "        eval_loss += loss.item()\n",
    "        \n",
    "        _ , pred = out.max(1)\n",
    "        num_correct = (pred==label).sum().item()\n",
    "        acc = num_correct / img.shape[0]\n",
    "        \n",
    "        eval_acc += acc\n",
    "        \n",
    "    eval_losses.append(eval_loss / len(test_data))\n",
    "    eval_acces.append(eval_acc / len(test_data))\n",
    "    \n",
    "    print('Epoch {}: \\nTrain Loss: {} \\tTrain Accuracy: {} \\nTest Loss: {} \\tTest Accuracy: {}\\n'.format(\n",
    "        epoch+1, train_loss / len(train_data),train_acc / len(train_data), eval_loss / len(test_data), eval_acc / len(test_data)))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:pytorch_py38]",
   "language": "python",
   "name": "conda-env-pytorch_py38-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
